package info.javatech.jmf.common.components;

import java.util.*;
import javax.microedition.lcdui.*;

import info.javatech.jmf.common.components.*;

/**
 * An item that visualizes a table from <code>TableModel</code>.
 * <p/>
 * The table can be larger than a screen in both  directions - user can use
 * a cursor to scroll in all directions.
 * @author breh
 */
public class TableComponent extends CustomItem implements Runnable{
    
        class ColorSchema{

            private Hashtable /*<display,SystemColorSchema>*/ systemColorSchemas = new Hashtable(1); // usually one display is in charge

            private Display display;

            /**
             * Creates a new instance of SystemColorSchema
             */
            private ColorSchema(Display display) {        
                this.display = display;
            }

            /**
             * Gets SystemColorSchema for given display 
             */
            public ColorSchema getForDisplay(Display display) {
                if (display == null) throw new IllegalArgumentException("Display parameter cannot be null");
                ColorSchema schema = (ColorSchema)systemColorSchemas.get(display);
                if (schema == null) {
                    schema = new ColorSchema(display);
                    systemColorSchemas.put(display,schema);            
                }
                return schema;
            }


            public int getColor(int aColorSpecifier) {
                return display.getColor(aColorSpecifier);
            }

            public Image getBackgroundImage() {
                return null;
            }

            public int getBackgroundImageAnchorPoint() {
                return Graphics.TOP | Graphics.LEFT;
            }

            public boolean isBackgroundImageTiled() {
                return false;
            }

            public boolean isBackgroundTransparent() {
                return false;
            }
    
            /**
             * Utility method for painting background do given Graphics object (using current
             * clipping area). This method is able to paint background, including image
             * and including tiling the image.
             * @param g - Graphics to be used to draw background.
             * @param includeImage - when true, the implementation also draws a background
             * image if specified, when false, the background image is ignored.
             */
            public void paintBackground(Graphics g, boolean includeImage) {
                if (g == null) throw new IllegalArgumentException("Graphics parameter cannot be null");
                int currentColor = g.getColor();
                final int x = g.getClipX();
                final int y = g.getClipY();
                final int width = g.getClipWidth();
                final int height = g.getClipHeight();

                if (!this.isBackgroundTransparent()) {
                     // fill the background with background color (only if the background is not transparent)
                    g.setColor(this.getColor(Display.COLOR_BACKGROUND));
                    g.fillRect(x,y,width,height);
                }        

                final Image backgroundImage = this.getBackgroundImage();
                if ((backgroundImage != null) && (includeImage)) {
                    if (this.isBackgroundImageTiled()) {
                        // compute width/height stuff
                        final int imageWidth = backgroundImage.getWidth();
                        final int imageHeight = backgroundImage.getHeight();
                        final int cx = width/imageWidth;
                        final int cy  = height/imageHeight;
                        //final int rx = width % imageWidth;
                        //final int ry = width % imageHeight;
                        System.out.println("CX = "+cx+"CY ="+cy);
                        for (int i=0; i <= cx; i++) {
                            for (int j=0; j <= cy; j++) {
                                g.drawImage(backgroundImage,i*imageWidth,j*imageHeight,Graphics.LEFT | Graphics.TOP);
                            }
                        }
                    } else {
                        final int backgroundImageAnchorPoint = this.getBackgroundImageAnchorPoint();
                        int ix = x;
                        int iy = y;
                        ix = (backgroundImageAnchorPoint & Graphics.RIGHT) > 0 ? width : 0;
                        ix = (backgroundImageAnchorPoint & Graphics.HCENTER) > 0 ? width / 2 : ix;
                        iy = (backgroundImageAnchorPoint & Graphics.BOTTOM) > 0 ? height : 0;
                        iy = (backgroundImageAnchorPoint & Graphics.VCENTER) > 0 ? height / 2 : iy;
                        g.drawImage(backgroundImage,ix,iy,backgroundImageAnchorPoint);
                    }
                } 
                // reset the color
                g.setColor(currentColor);
            }
        }
    
/*
 
    Variables:
 
 *  sortOrder 	
        int(enumeration(int))    
        0 - ASC,
        1 - DESC                
        Stores the sort order value for current sorted column 
    
 *  sortColumn 	
        int                 
        0 - num of table col - 1	
        number of sorted column (0-based) 
    
 *  sortSource 	
        int(enumeration(int))
        0 - CLIENT
        1 - SERVER
        Determines the place where sort happens 
    
 *  sortScope 
        int(enumeration(int))    
        0 - to ONE
        1 - to ALL	
        Determines the scope of sorting 
 
 */        

    /*public static final int SORT_ORDER_ASC = 0;
    public static final int SORT_ORDER_DESC = 1;
    public static final int SORT_SOURCE_CLIENT = 0;
    public static final int SORT_SOURCE_SERVER = 1;
    public static final int SORT_SCOPE_ONE = 0;
    public static final int SORT_SCOPE_ALL = 1;
    public static final int PAGINATION_SOURCE_CLIENT = 0;
    public static final int PAGINATION_SOURCE_SERVER = 1;
    public static final int SELECTION_MODE_CELL = 0;
    public static final int SELECTION_MODE_SINGLEROW = 1;
    public static final int SELECTION_MODE_MULTIROW = 2;*/
    
    
    
    //--------SORTING PROPETRIES
    private int sortOrder = TableComponentConstants.SORT_ORDER_ASC;
    private int sortColumn = -1;
    private int sortSource = TableComponentConstants.SORT_SOURCE_CLIENT;
    private int sortScope = TableComponentConstants.SORT_SCOPE_ONE;
        
    //--------PAGINATION PROPETRIES
    private int currentPage;
    private int pageSize;
    private int paginationSource = TableComponentConstants.PAGINATION_SCOPE_CLIENT;
    
    //--------SELECTION PROPETRIES
    private int selectionMode = TableComponentConstants.SELECTION_MODE_CELL;
    private int[] selection;
   
    
    //private Vector modelListeners = new Vector();
    private String[][] values = new String[0][0];
    private String[] columnNames = new String[0];
    

    
    private int cols;
    private int rows;
	
	
    private static final boolean DEBUG = false;


    private int tableRows = 0; // count of table rows (from model)
    private int tableCols = 0; // count of table columns (from model)

    private String title = null; // title of the table

    private boolean borders = true; // is using borders?
    private boolean usingHeaders = true; // is using usingHeaders (column names)?

    private final Display display; // /display used to get system colors

    private static final Font STATIC_TEXT_FONT = Font.getFont(Font.FACE_MONOSPACE, Font.STYLE_PLAIN, Font.SIZE_SMALL); // standard font for items to draw text

    // default font for title - using standard font with Bold style
    private static final Font DEFAULT_TITLE_FONT = Font.getFont(STATIC_TEXT_FONT.getFace(),
                            STATIC_TEXT_FONT.getStyle() | Font.STYLE_BOLD,
                            STATIC_TEXT_FONT.getSize());;

    // default font for title - using standard font with Bold style
    private static final Font DEFAULT_HEADERS_FONT = Font.getFont(STATIC_TEXT_FONT.getFace(),
                            STATIC_TEXT_FONT.getStyle() | Font.STYLE_BOLD,
                            STATIC_TEXT_FONT.getSize());;	

    // values are using standard font
    private static final Font DEFAULT_VALUES_FONT = STATIC_TEXT_FONT;

    private Font titleFont = DEFAULT_TITLE_FONT; // titleFont
    private Font headersFont = DEFAULT_HEADERS_FONT; // usingHeaders (column names) font
    private Font valuesFont = DEFAULT_VALUES_FONT; // values font

    //private SimpleTableModel model = new SimpleTableModel(); // model

    private int[] colWidths; // array of individual column widths
    private int[] rowHeights; // array of individual row height - currently using the same height for all 
    private int totalColWidth; // total width of all columns

    private int defaultCellWidth, defaultCellHeight; // default cell width/heights

    private static final int CELL_PADDING = 2; // size text cell padding
    private static final int DOUBLE_CELL_PADDING = 2*CELL_PADDING; // double cell padding helper for computation	
    private static final int BORDER_LINE_WIDTH = 1; // size of border line

    //private boolean alwaysDrawTitle = false; // always draw title even the table is scrolled // not used in this release
    //private boolean alwaysDrawHeaders = true; // always draw usingHeaders (column names) // not used in this release 

    private boolean cursorOn = false; // should cursor be drawn?	
    private int cursorCellX, cursorCellY; // coordinates of cursor	
    private int[] cursorSelectedRows;

    private int viewCellX = 0; // X value of a cell which is visible on the left
    private int viewCellX2 = 0; // X value of a cell which is visible on the right

    private boolean tableFitsHorizontally = true; // does the whole table fit on the screen?

    private boolean firstPaint = true;

    private int sizeWidth = 0;   // current size assigned to this item - some implementations (Nokia) still
    private int sizeHeight = 0;  // keeps calling sizeChanged() with the same size, so I keep these values
                                                             // to not call repaint() when it is not neccessary
                                                             
    private int selectedBeforeHeaderForSorting = -1;
    
    private int BORDER_COLOR = 0x000000;
    //private int FOREGROUND_COLOR = 0xffffff;
    //private int HIGHLIGHT_FOREGROUND_COLOR = 0x666666;
    
    
    private ColorSchema colorSchema; // color schema in use

    private int waiting = -1;
    
    Thread curr_thread;
    
    /**
     * Creates a new instance of <code>TableItem</code> with a model.
     *
     * @param display non-null display parameter.
     * @param label label for the item
     * @param model a <code>TableModel</code> to be visualized by this item
     * @throws java.lang.IllegalArgumentException if the display parameter is null
     */
    public TableComponent(Display display, String label) throws IllegalArgumentException {
            this(display,label,null);
    }


    /**
     * Creates a new instance of <code>TableItem</code> with a model.
     *
     * @param display non-null display parameter.
     * @param label label for the item
     * @param model a <code>TableModel</code> to be visualized by this item
     * @param colorSchema a color schema to be used. If set to null, SystemCOlorSchema will be used
     * @throws java.lang.IllegalArgumentException if the display parameter is null
     */
    public TableComponent(Display display, String label, ColorSchema colorSchema) throws IllegalArgumentException {
        super(label);

        if (display == null) throw new IllegalArgumentException ("display parameter cannot be null");
        this.display = display;
        updateDefaultCellSize();	
        recomputeModelValues();

        setColorSchemaImpl(display,colorSchema);
        
        
    }        

    /**
     * Sets color schema to be used with this component. If set to null
     * SystemColorSchema will be used
     */
    public void setColorSchema(ColorSchema colorSchema) {
        setColorSchemaImpl(display,colorSchema);
        repaint();
    }


    /**
     * Gets color schema currently in use
     */
    public ColorSchema getColorSchema() {
        return colorSchema;
    }


    /**
     * Gets title of the table.
     * @return title string or null if there is no title defined
     */
    public String getTitle() {
            return title;
    }

    /**
     * Sets title of the table. The difference between title and label
     * specified in the constructor is, that the title appears in the table (it 
     * is surrounded by the borders). Title can be null if no title should
     * be shown.
     * <p/>
     * The table is automatically repainted when a new title is set.
     * @param tableTitle title for the table. Can be null
     */
    public void setTitle(String tableTitle) {
        if (this.title != tableTitle) {
            this.title = tableTitle;
            repaint();
        }
    }	

    /**
     * Should the table use borders
     * @return true if the table will be painted with borders. False if it will be
     * borderless.
     */
    public boolean isBorders() {
            return borders;
    }

    /**
     * Sets state whether the table should be visualized with or without borders.
     *  <p/>
     * The table is automatically repainted when the state is changed.
     * @param showBorders true if the borders should be painted, false otherwise
     */
    public void setBorders(boolean showBorders) {
            if (this.borders != showBorders) {
                    this.borders = showBorders;
                    repaint();
            }
    }	


    /**
     * Sets a new model to the table. The table gets automatically repainted
     * accordingly to a new model.
     * @param model a new model to be visualized - cannot be null
     * @throws java.lang.IllegalArgumentException if the model parameter is null
     */
    /*public void setModel(SimpleTableModel model) throws IllegalArgumentException {
        
        if (this.model != null) {
                this.model.removeTableModelListener(this);
        }
        if (model == null) throw new IllegalArgumentException("model parameter cannot be null");
        if (model != null) {
                if (model.getRowCount() < 0) throw new IllegalArgumentException("model cannot have negative number of rows");
                if (model.getColumnCount() < 0) throw new IllegalArgumentException("model cannot have negative number of cols");
                model.addTableModelListener(this);
        }
        this.model = model;
        recomputeModelValues();
        invalidate();
    }
    */


    /**
     * Gets font used to paint the table title
     * @return title font
     */
    public Font getTitleFont() {
            return titleFont;
    }

    /**
     * Sets the font to be used to paint title. If null is specified,
     * the default font (bold version of static text font) will be used.
     * <p/>
     * When the font changes, the table gets automatically repainted.
     * @param titleFont font to be used for painting table title, might be null.
     */
    public void setTitleFont(Font titleFont) {
            if (this.titleFont != titleFont) {
                    if (titleFont != null) {
                            this.titleFont = titleFont;
                    } else {
                            this.titleFont = DEFAULT_TITLE_FONT;
                    }
                    repaint();
            }
    }

    /**
     * Gets font used to paint the table cell values
     * @return values font
     */
    public Font getValuesFont() {
            return valuesFont;
    }

    /**
     * Sets the font to be used to paint title. If null is specified,
     * the default font (static text font) will be used.
     * <p/>
     * When the font changes, the table gets automatically repainted.
     * @param valuesFont font used for painting values, might be null
     */
    public void setValuesFont(Font valuesFont) {
            if (this.valuesFont != valuesFont) {
                    if (valuesFont != null) {
                            this.valuesFont = valuesFont;
                    } else {
                            this.valuesFont = DEFAULT_VALUES_FONT;
                    }
                    updateDefaultCellSize();
                    recomputeModelValues();
                    repaint();
            }
    }	


    /**
     * Gets font used to paint the column names (headers) of the table
     * @return headers font
     */
    public Font getHeadersFont() {
            return headersFont;
    }

    /**
     * Sets the font to be used to paint title. If null is specified,
     * the default font (bold static text font) will be used.
     * <p/>
     * When the font changes, the table gets automatically repainted.
     * @param headersFont font used for painting column names (headers), might be null
     */
    public void setHeadersFont(Font headersFont) {
            if (this.headersFont != headersFont) {
                    if (headersFont != null) {
                            this.headersFont = headersFont;
                    } else {
                            this.headersFont = DEFAULT_HEADERS_FONT;
                    }
                    repaint();
            }
    }


    /**
     * Gets the row position of the cursor in the table.
     * @return selected cell row
     */
    public int getSelectedCellRow() {
            return cursorCellY;
    }


    /**
     * Gets the column position of the cursor in the table.
     * @return selected cell column
     */
    public int getSelectedCellColumn() {
            return cursorCellX;		
    }


    /*
    private static Font getSafeFont(Font font) {
            return font == null ? Font.getDefaultFont() : font;
    }*/


/**
 * implementation of the abstract method
 * @return minimal content height
 */
protected int getMinContentHeight() {		
            int sum = (tableRows * (defaultCellHeight + BORDER_LINE_WIDTH)) + BORDER_LINE_WIDTH;
            if (title != null) {
                    sum += getTitleFont().getHeight() + DOUBLE_CELL_PADDING + BORDER_LINE_WIDTH;
            } 
            if (usingHeaders) {
                    sum += getHeadersFont().getHeight() + DOUBLE_CELL_PADDING + BORDER_LINE_WIDTH;
            }
            if (getLabel() != null) { // this is rather hack - I don't know the size of the label !!!
                    sum += Font.getDefaultFont().getHeight();
            }
    return sum;
}

/**
 * implementation of the abstract method
 * @return minimal contnent width
 */
protected int getMinContentWidth() {
    recomputeModelValues();
            int sum = colWidths.length * BORDER_LINE_WIDTH + BORDER_LINE_WIDTH;		
            int result = 0;
            for (int i=0; i < colWidths.length; i++) {
                    sum += colWidths[i];
            }	
            result = sum;
            if (title != null) {
                    int titleWidth = getTitleFont().stringWidth(title);
                    if (titleWidth > result) {
                            result = titleWidth + DOUBLE_CELL_PADDING;
                    }
            } 
            if (getLabel() != null) { // this is rather hack - I don't know the size of the label !!!
                    int labelWidth = Font.getDefaultFont().stringWidth(getLabel());
                    if (labelWidth > result) {
                            result = labelWidth;
                    }
            }
    return result;
}

/**
 * implementation of the abstract method
 * @param width 
 * @return preferred contnent height
 */
protected int getPrefContentHeight(int width) {
                    return getMinContentHeight();
}

/**
 * implementation of the abstract method
 * @param height 
 * @return preferred content width
 */
protected int getPrefContentWidth(int height) {
            return getMinContentWidth();
}

    /**
     * implementation of the abstract method
     * @param g 
     * @param width 
     * @param height 
     */
    protected void paint(Graphics g, int width, int height) {

            //if (doPaint) {
            firstPaint = false;

            if (DEBUG) System.out.println("\n@@@@@@@@@@@@@@ paint: width="+width+" height="+height+""+"::: "+this);
            if (DEBUG) System.out.println("@@@@@@@@@@@@@@ paint: Clip: X="+g.getClipX()+" Y="+g.getClipY()+" W="+g.getClipWidth()+" H="+g.getClipHeight());
            invalidate();
            
            tableFitsHorizontally = totalColWidth <= width; // does the table horizontally fit to the given drawing area

            boolean rightmostColumnFullyVisible = true; // is the table scrolled to the right (i.e. the rightmost column is fully visible?)

            int currentColor = g.getColor();

            // paint the background based on color schema
            getColorSchema().paintBackground(g, false);
            // clear the area
            //g.setColor(getColorSchema().getColor(Display.COLOR_BACKGROUND));
            //g.fillRect(g.getClipX(), g.getClipY(), g.getClipWidth(), g.getClipHeight());
            //g.fillRect(0, 0, width, height);

            int hy = height - 1; // actual height ?
            int wx = width  - 1; // actual width ?

            // get height of title
            int titleHeight = 0;		
            if (title != null) {
                    titleHeight = getTitleFont().getHeight() + DOUBLE_CELL_PADDING; // title height
            }

            // get height of headers
            int headersHeight = 0;
            if (usingHeaders) {
                    headersHeight =  getHeadersFont().getHeight() + DOUBLE_CELL_PADDING; // headers height
            }

            int viewWidth = wx; // current visible width of the table

            int actualTableWidth = width; // actual table width (based on cell computation), used to draw borders

            if (/*(model != null) &&*/ (tableRows != 0) && (tableCols != 0)) { 

                    // update visible width of the table based on columns to be drawn
                    //viewWidth = wx;
                    if (viewCellX2 < (tableCols - 1)) {
                            viewWidth = wx;
                    } else {
                            viewWidth = 0;
                            for (int i=viewCellX; (i <= viewCellX2) && (i < tableCols); i++) {
                                    viewWidth+= colWidths[i];
                            }
                            if (viewWidth > wx) {
                                    viewWidth = wx;
                            }
                    }

                    // paint cursor
                    if (cursorOn) {
                            final int x = getCursorX();
                            int y = getCursorY();

                            // add title height if to be drawn
                            if (title != null) {
                                    y += titleHeight + BORDER_LINE_WIDTH;
                            }
                            // add headers height if to be drawn
                            if (usingHeaders) {
                                    y += headersHeight + BORDER_LINE_WIDTH;
                            }

                            final int w = colWidths[cursorCellX];
                            final int h = defaultCellHeight;
                            //g.setColor(cursorColor);
                            // draw cursor ...
                            g.setColor(getColorSchema().getColor(Display.COLOR_HIGHLIGHTED_BACKGROUND));
                            g.fillRect(x, y,  w, h);
                            
                            
                    }
                    
                    /*makes selected one or few rows*/
                    if (selectionMode == TableComponentConstants.SELECTION_MODE_SINGLE_ROW || selectionMode == TableComponentConstants.SELECTION_MODE_MULTI_ROW)
                    if (cursorSelectedRows != null && cursorSelectedRows.length > 0){

                        for (int i = 0; i < cursorSelectedRows.length; i++){
                           //i - number of row

                            int offY = getCursorY(cursorSelectedRows[i]);
                            int offX = 0;

                            if (title != null) {
                                offY += titleHeight + BORDER_LINE_WIDTH;
                            }
                            if (usingHeaders) {
                                offY += headersHeight + BORDER_LINE_WIDTH;
                            }


                            for (int j = 0; j < cols; j++){
                                //j - number of column 

                                offX = getCursorX(j);

                                g.setColor(getColorSchema().getColor(Display.COLOR_HIGHLIGHTED_BACKGROUND));
                                g.fillRect(offX, offY,  colWidths[j], defaultCellHeight);

                            }

                        }

                    }


                    int y = 0; // y coordinate to be used to draw headers/values
                    // add title height if to be drawn
                    if (title != null) {
                            y += titleHeight + BORDER_LINE_WIDTH;
                    }
                    // add headers height if to be drawn
                    // draw headers
                    if (usingHeaders) {
                            g.setColor(getColorSchema().getColor(Display.COLOR_FOREGROUND));
                            g.setFont(getHeadersFont());			

                            int x = BORDER_LINE_WIDTH;
                            final int gy = y + CELL_PADDING; // actual y used to be draw the text (icludes padding)
                            for (int j=viewCellX; j < getColumnCount(); j++) { 
                                    viewCellX2 = j;

                                    final Object value = getColumnName(j);					
                                    final int colWidth = colWidths[j];

                                    if (value != null) {
                                            g.drawString(value.toString(), x+colWidth/2 + /*for sorting*/(sortColumn == -1?0:3), gy, Graphics.TOP | Graphics.HCENTER);
                                    }
                                    
                                    /*
                                        draw sorting propetries
                                     */
                                    if (sortColumn != -1){
                                        if (j == sortColumn){
                                            g.drawLine(x + 3, gy - CELL_PADDING + headersHeight / 2 - headersHeight / 4, x + 3, gy + headersHeight / 2 + headersHeight / 4);
                                            if (sortOrder ==TableComponentConstants.SORT_ORDER_ASC){
                                                g.drawLine(x + 3, gy + headersHeight / 2 + headersHeight / 4, x + 2, gy + headersHeight / 2 + headersHeight / 4 - 1);
                                                g.drawLine(x + 3, gy + headersHeight / 2 + headersHeight / 4, x + 4, gy + headersHeight / 2 + headersHeight / 4 - 1);
                                            }else
                                            if (sortOrder == TableComponentConstants.SORT_ORDER_DESC){
                                                g.drawLine(x + 3, gy - CELL_PADDING + headersHeight / 2 - headersHeight / 4, x + 2, gy - CELL_PADDING + headersHeight / 2 - headersHeight / 4 + 1);
                                                g.drawLine(x + 3, gy - CELL_PADDING + headersHeight / 2 - headersHeight / 4, x + 4, gy - CELL_PADDING + headersHeight / 2 - headersHeight / 4 + 1);
                                            }
                                        }
                                    }
                                    
                                    x += colWidth;
                                    if (x > width) {
                                            rightmostColumnFullyVisible = false;
                                            break;
                                    } 
                            }				
                            y += headersHeight + BORDER_LINE_WIDTH;
                    }		
                    //  draw values
                    g.setColor(getColorSchema().getColor(Display.COLOR_FOREGROUND));
                    g.setFont(getValuesFont());				

                    for (int i=0; (i < getRowCount()); i++) {

                            int x = BORDER_LINE_WIDTH + CELL_PADDING;
                            final int gy = y + CELL_PADDING; // actual y used to be draw the text (icludes padding)				

                            for (int j=viewCellX; j < getColumnCount(); j++) {
                                    viewCellX2 = j;
                                    Object value = getValue(j,i);
                                    if (value != null) {
                                            if ( cursorOn && ((j==cursorCellX) && (i == cursorCellY)) || isConsistInSelectedRow(i)) {
                                                    g.setColor(getColorSchema().getColor(Display.COLOR_HIGHLIGHTED_FOREGROUND));
                                                    g.drawString(value.toString(), x, gy, Graphics.TOP | Graphics.LEFT);
                                                    g.setColor(getColorSchema().getColor(Display.COLOR_FOREGROUND));
                                            } else {
                                                    g.drawString(value.toString(), x, gy, Graphics.TOP | Graphics.LEFT);
                                            }
                                    }
                                    x += colWidths[j];
                                            if (x > width) {
                                            rightmostColumnFullyVisible = false;
                                            break;
                                    }
                            }	
                            y += defaultCellHeight + BORDER_LINE_WIDTH;
                    }

                    // finally draw borders (if applicable)
                    if (isBorders()) {
                            g.setColor(getBorderColor()/*getColorSchema().getColor(Display.COLOR_BORDER)*/);
                            //int currentStrokeStyle = g.getStrokeStyle();
                            //g.setStrokeStyle(display.getBorderStyle(true));

                            y = titleHeight;

                            int totalTableHeight = tableRows * (defaultCellHeight + BORDER_LINE_WIDTH) + titleHeight; // total totalTableHeight
                            if (usingHeaders) {
                                    totalTableHeight += headersHeight + BORDER_LINE_WIDTH; // add it to total table height
                            }
                            
                            // vertical lines
                            int x =0; 
                            for (int i=viewCellX; (i < tableCols) && (x < width); i++) {
                                    x+=colWidths[i];
                                    /*
                                     *  draw sort box round column
                                     */
                                    if (sortScope == TableComponentConstants.SORT_SCOPE_ONE){
                                        if (i == sortColumn - 1){
                                            g.drawLine(x + 1, titleHeight, x + 1, totalTableHeight);                                            
                                        }
                                        if (i == sortColumn){
                                            g.drawLine(x - 1, titleHeight, x - 1, totalTableHeight);                                            
                                        }
                                    }
                                    g.drawLine(x, titleHeight, x, totalTableHeight);
                                    actualTableWidth = x; // getting value of the leftmost table line - out actualTableWidth
                            }
                            if ((titleHeight > 0) && rightmostColumnFullyVisible) {
                                    g.drawLine(x, 0, x, titleHeight);
                            }				

                            // horizontal lines
                            
                            // first line on the top
                            g.drawLine(0,0,actualTableWidth,0);
                            if (sortColumn != -1)
                            if (sortScope == TableComponentConstants.SORT_SCOPE_ONE){
                                int temp = 0;
                                for (int i = viewCellX ; i < sortColumn; i++){
                                    temp += colWidths[i];
                                }
                                g.drawLine(temp, 1, temp + colWidths[sortColumn], 1);
                                g.drawLine(temp, totalTableHeight - 1, temp + colWidths[sortColumn], totalTableHeight - 1);
                            }else
                            if (sortScope == TableComponentConstants.SORT_SCOPE_ALL){
                                g.drawRect(1, 1, actualTableWidth - 2, totalTableHeight - 2);
                            }
                            // draw header line
                            if (usingHeaders) { 
                                    g.drawLine(0, y, actualTableWidth, y);					
                                    y += headersHeight + BORDER_LINE_WIDTH;
                            }

                            // draw value lines
                            for (int i=0; (i <= tableRows) && (y <= totalTableHeight); i++, y+=defaultCellHeight+BORDER_LINE_WIDTH) {
                                    g.drawLine(0, y, actualTableWidth, y);
                            }

                            // draw the remaining left and right line
                            if (sortScope == TableComponentConstants.SORT_SCOPE_ONE){
                                if (sortColumn == viewCellX){
                                    g.drawLine(1, 0, 1, totalTableHeight);
                                }
                            }
                            g.drawLine(0, 0,  0, totalTableHeight);			
                            //g.setStrokeStyle(currentStrokeStyle);
                    }
            }		
            // draw title
            if (title != null) {
                    g.setColor(getColorSchema().getColor(Display.COLOR_FOREGROUND));
                    g.setFont(titleFont);
                    // first is table title
                    g.drawString(title, actualTableWidth/2, BORDER_LINE_WIDTH + CELL_PADDING, Graphics.TOP | Graphics.HCENTER);
                    int half = titleHeight / 2;
                    if (viewCellX > 0) {
                            // draw triangle on the left
                            g.drawLine(2, half, 5, half - 2); 
                            g.drawLine(2, half, 5, half + 2);
                    } 
                    if (! rightmostColumnFullyVisible) {
                            // draw triangle on the right
                            int rx = actualTableWidth - 2;
                            g.drawLine(rx, half, rx - 3, half - 2);
                            g.drawLine(rx, half, rx - 3, half + 2);
                    }
            }	
            
            if (waiting == 0){
                g.setColor(255,0,0);
                g.fillRect(0, 0, DEFAULT_VALUES_FONT.stringWidth("Sorting..."), defaultCellHeight);
                g.setColor(0, 0, 0);
                g.drawString("Sorting...", 0, 0, Graphics.TOP | Graphics.LEFT);
            }else
            if (waiting == 1){
                g.setColor(255,0,0);
                g.fillRect(0, 0, DEFAULT_VALUES_FONT.stringWidth("Selecting..."), defaultCellHeight);
                g.setColor(0, 0, 0);
                g.drawString("Selecting...", 0, 0, Graphics.TOP | Graphics.LEFT);
            }
            
            g.setColor(currentColor);
            /*} else {
                    if (DEBUG) System.out.println("\n@@@@@@@@@@@@@@ NO PAINT: width="+width+" height="+height+"");
                    if (DEBUG) System.out.println("@@@@@@@@@@@@@@ NO PAINT : Clip: X="+g.getClipX()+" Y="+g.getClipY()+" W="+g.getClipWidth()+" H="+g.getClipHeight());
            }*/
    }

    /**
     * implementation of the abstract method - if the item size has changed, simply repaint the table
     * @param w 
     * @param h 
     */
    
    //public int getHighForegrColor(){
    //    return HIGHLIGHT_FOREGROUND_COLOR;
    //}
    
    //public int getForegrColor(){
    //    return FOREGROUND_COLOR;
    //}
    
    public int getBorderColor(){
        return BORDER_COLOR;
    }
    
    
    
    protected void sizeChanged(int w, int h) {
            if (DEBUG) System.out.println("^^^^^^^^^^ sizeChanged : w="+w+" h="+h+" ::: "+this);
            if ((! firstPaint) && (w > 0) && (h > 0) && ( w != sizeWidth) && ( h != sizeHeight )) {
                    sizeWidth = w;
                    sizeHeight = h;
                    //repaint();
            }
    }

    /**
     * implementation of the abstract method
     */
    protected boolean traverse(int dir, int viewportWidth, int viewportHeight, int[] visRect_inout) {
            if (DEBUG) System.out.println("\n****************************\nTraversal occured: dir = "+dir+" ::: "+this);
            if (DEBUG) System.out.println("traverse:viewportWidth = "+viewportWidth+", viewportHeight = "+viewportHeight);
            if (DEBUG) System.out.println("traverse:visRect = ["+visRect_inout[0]+","+visRect_inout[1]+","+visRect_inout[2]+","+visRect_inout[3]+"]");
            if (DEBUG) System.out.println("traverse: cursorCellX = "+cursorCellX+", cursorCellY = "+cursorCellY);
            /*if (model == null) {
                    // model is not set - do not traverse ...
                    if (DEBUG) System.out.println("traverse: model is not set - returning\n\n");
                    return false;
            } // else*/		
            boolean retValue = false; // what should be returned - traversal occured (true), did not occured (false)
            boolean repaint = false; // should be the component repainted?
            if (cursorOn == false) {			
                    if (DEBUG) System.out.println("traverse: cusorOn was false, entering item ..., dir = "+dir);
                    //cursorCellX = 0;
                    if (dir == Canvas.UP) {
                        cursorCellY = tableRows - 1;
                    } else if (dir == Canvas.DOWN) {
                        cursorCellY = 0;
                    } else if (dir == Canvas.RIGHT) {	
                        cursorCellY = 0;
                        cursorCellX = 0;
                    } else if (dir == Canvas.LEFT) {	
                        cursorCellY = tableRows - 1;
                        cursorCellX = tableCols - 1;
                        viewCellX2 = cursorCellX;
                    } else {                            
                        // this can happen only when a traverseIn was called
                        // after traverseOut
                        // do not move cursor - simply return back to the item and repaint
                    }
                    cursorOn = true; // cursor is on !!!
                    retValue = true;
                    repaint = true;
            } else {
                    // move cursor
                    if (dir == Canvas.UP) {
                            cursorCellY--;
                    } else if (dir == Canvas.DOWN) {
                            cursorCellY++;
                    } else if (dir == Canvas.LEFT) {
                            cursorCellX--;
                    } else if (dir == Canvas.RIGHT) {
                            cursorCellX++;
                    }
                    // now check if we are inbounds
                    if (cursorCellX < 0) {
                            cursorCellX = 0;
                            retValue =  true;
                            repaint = false;
                    } else if (cursorCellX >= tableCols) {
                            cursorCellX = tableCols - 1;
                            retValue =  true;
                            repaint = false;
                    }
                    if ((cursorCellY >= 0) && (cursorCellY < tableRows)) {
                            if (DEBUG) System.out.println("traverse: cursorY in bounds");
                            retValue = true;
                            repaint = true;
                    } else {
                            if (DEBUG) System.out.println("traverse: cursorY out of bounds");
                            retValue = false;
                            repaint = false;
                            if (cursorCellY < 0) {
                                    cursorCellY = 0;
                            } else {
                                    cursorCellY = tableRows - 1;
                            }
                    }
            }		
            if (DEBUG) System.out.println("traverse: updating visRect");
            /*
            //visRect_inout[0] = 0;
            visRect_inout[1] = 0;
            //visRect_inout[2] = viewportWidth;
            //visRect_inout[3] = viewportHeight;
            int cy = getCursorY() + 2 * defaultCellHeight;
            if (cy > visRect_inout[3]) {
                    visRect_inout[1] = cy - viewportHeight;
                    //visRect_inout[3] = viewportHeight;
            }*/
            //visRect_inout[0] = getCursorX(); // we don't care about X 



            int cursorY = getCursorY();
            int cursorHeight = defaultCellHeight;
            int headersHeight = 0;
            if (title != null) {
                    headersHeight += getTitleFont().getHeight() + DOUBLE_CELL_PADDING + BORDER_LINE_WIDTH;
            }
            // add headers height if to be drawn
            if (usingHeaders) {
                    headersHeight += getHeadersFont().getHeight() + DOUBLE_CELL_PADDING + BORDER_LINE_WIDTH;
            }

            //if ((cursorY + cursorHeight + headersHeight) > viewportHeight) {
                    // y
                    visRect_inout[1] = cursorY + headersHeight;
                    visRect_inout[3] = defaultCellHeight;
                    // x
                    visRect_inout[0] = getCursorX();
                    visRect_inout[2] = defaultCellWidth;			
            /*} else {
                    //visRect_inout[1] = 0;
                    visRect_inout[0] = getCursorX();
                    visRect_inout[2] = defaultCellWidth;			
                    //visRect_inout[3] = viewportHeight;
                    visRect_inout[3] = defaultCellHeight;
            }*/



            /*
             visRect_inout[1] = getCursorY() + defaultCellHeight;
            int horizontalShift = 0;
            if (title != null) {
                    horizontalShift = getTitleFont().getHeight() + CELL_PADDING + BORDER_LINE_WIDTH;
            }
            if (usingHeaders) {
                    horizontalShift += getHeadersFont().getHeight() + CELL_PADDING + BORDER_LINE_WIDTH;
            }
            if (dir == Canvas.UP) {
                    visRect_inout[1] -= horizontalShift;
            } else if (dir == Canvas.DOWN) {
                    visRect_inout[1] += horizontalShift;
            }

            // visRect_inout[2] = colWidths[cursorCellX]; // we don't care about X
            visRect_inout[3] = defaultCellHeight;
             */
            //if (DEBUG) System.out.println("traverse: tableFits = "+tableFitsHorizontally);
            if (! tableFitsHorizontally) {			
                    repaint = true;
                    if (cursorCellX >= viewCellX2) {
                            int sum = BORDER_LINE_WIDTH + CELL_PADDING;;
                            int i = viewCellX2;
                            while ((sum <= viewportWidth) && (i >= 0)) {
                                    sum += colWidths[i] + BORDER_LINE_WIDTH;
                                    i--;
                            }
                            viewCellX = i + 2;
//				if (viewCellX >= tableCols) {
//					viewCellX = tableCols - 1;
//				} 

                            if (viewCellX > viewCellX2) {
                                    viewCellX = cursorCellX;
                            }
                    } else if (cursorCellX < viewCellX) {
                            viewCellX --;
                    }
            }
            if (repaint) {
                    repaint();
            }
            if (DEBUG) System.out.println("traverse: returning: "+retValue+", cursorCellX = "+cursorCellX+", cursorCellY = "+cursorCellY);
            if (DEBUG) System.out.println("traverse: returning visRect = ["+visRect_inout[0]+","+visRect_inout[1]+","+visRect_inout[2]+","+visRect_inout[3]+"]+\n\n");
            return retValue;
    }
	
    /**
     * implementation of the abstract method
     */
    protected void traverseOut() {
            if (DEBUG) System.out.println("---------  traverseOut !!! ::: "+this);
            super.traverseOut();
            cursorOn = false;
            repaint();
    }
	
	
	
    /*****
     *
     * private methods 
     * 
     ******/	

    private int getCursorX() {
            int x = 0;
            for (int i=viewCellX; i < cursorCellX; i++) {
                    x += colWidths[i];
            }
            return x;
    }
    
    private int getCursorX(int cellNumberX){
        int x = 0;
        for (int i=viewCellX; i < cellNumberX; i++) {
                x += colWidths[i];
        }
        return x;
    }
    
    private int getCursorY(int cellNumberY){
        return cellNumberY * (defaultCellHeight + BORDER_LINE_WIDTH);
    }


    private int getCursorY() {
            return cursorCellY * (defaultCellHeight + BORDER_LINE_WIDTH);
    }

    /**
     * recomputes cell sizes based on set data and fonts
     */
    private void recomputeModelValues() {
            /*if (model != null) {*/
            tableRows = getRowCount();
            tableCols = getColumnCount();			
            /*} else {
                    tableCols = 0;
                    tableRows = 0;	
                    usingHeaders = false;
            }*/
            // cells
            colWidths = new int[tableCols];
            for (int i=0; i < tableCols; i++) {
                    colWidths[i] = defaultCellWidth;
            }		
            /*if (model != null) {*/		
            // values
            final int columnCount = getColumnCount();
            final int rowCount = getRowCount();
            for (int i=0; i < columnCount; i++) {
                    for (int j=0; j < rowCount; j++) {
                            Object value = getValue(i, j);
                            if (value != null) {
                                    int width = getValuesFont().stringWidth(value.toString()) + DOUBLE_CELL_PADDING;
                                    if (width > colWidths[i]) {
                                            colWidths[i] = width;
                                    }
                            }
                    }
            }
            // column header (they might be bigger) 
            usingHeaders = isUsingHeaders();
            if (isUsingHeaders()) {
                    for (int i=0; i < columnCount; i++) {
                            String columnName = getColumnName(i);
                            if (columnName != null) {
                                    int width = getHeadersFont().stringWidth(columnName.toString()) + DOUBLE_CELL_PADDING + /*for sorting*/(sortColumn == -1?0:5);
                                    if (width > colWidths[i]) {
                                            colWidths[i] = width;
                                    }
                            }
                    }
            }
            /*}*/

            // compute total column widthj
            totalColWidth = 0;
            for (int i=0; i < colWidths.length; i++) {
                    totalColWidth += colWidths[i];
            }

    }	
	
    /*
     * recomputes base defaultCellWidth, defaultCellHeight based on current values font
     */
    private void updateDefaultCellSize() {
            defaultCellWidth = getValuesFont().stringWidth("X") + DOUBLE_CELL_PADDING;
            defaultCellHeight = getValuesFont().getHeight() + DOUBLE_CELL_PADDING;
    }	

        
    /**
     * Sets color schema. If null, creates a new SystemColorSchema based on the display
     */
    private void setColorSchemaImpl(Display display, ColorSchema colorSchema) {
        if (colorSchema != null) {
            this.colorSchema = colorSchema;
        } else {
            this.colorSchema = getForDisplay(display);
        }
    }
    
    public ColorSchema getForDisplay(Display display) {
        Hashtable /*<display,SystemColorSchema>*/ systemColorSchemas = new Hashtable(1); // usually one display is in charge
        if (display == null) throw new IllegalArgumentException("Display parameter cannot be null");
        ColorSchema schema = (ColorSchema)systemColorSchemas.get(display);
        if (schema == null) {
            schema = new ColorSchema(display);
            systemColorSchemas.put(display,schema);            
        }
        return schema;
    }
	
	
    /**
     * Listener for changes of the model. Just repaints the table when 
     * any change happened to the table model of this table.
     * @param changedModel 
     */
    /*public void tableModelChanged(SimpleTableModel changedModel) {
        if (changedModel == model) {
            recomputeModelValues();
            invalidate();
            //repaint();
        }
    }*/
    
    /**
     * Gets number of columns of the supplied values matrix.
     * @return values matrix column count
     */
    public int getColumnCount() {
        return cols;
    }

    /**
     * Gets number of rows of the supplied values matrix.
     * @return values matrix row count
     */
    public int getRowCount() {
        return rows;
    }


    /**
     * Sets the value to the defined row and column of the model. 
     * <p>
     * Please note, this method does not call fireTableModelChanged method 
     * automatically, so you have to call it manually if you would like to redraw
     * the table. This is designed in this way, because of the performance reasons
     * - you might want to update several values at a time and repaint the 
     * table at the end of the update.
     *
     * @throws java.lang.IllegalArgumentException if the values are not defined, or 
     * the specifed row or column is larger than the size of the values.
     */
    public void setValue(int col, int row, String value) throws IllegalArgumentException {
        if (values == null) throw new IllegalArgumentException("No values set to the model");
        if (values.length < row) throw new IllegalArgumentException("Specified row ("
                        + row + ") is larger than the number of rows available in values ("
                        + values.length + ").");
        if ( (values[row].length < col)) throw new IllegalArgumentException("Specified column ("
                        + col + ") is larger than the number of columns available in values ("
                        + values[row].length + ").");
        // change the value
        values[row][col] = value;
        recomputeModelValues();
        repaint();
    }


    /**
     * Sets the values of the model. Values of this model have to be a rectangular matrix - 
     * this means all rows have to have the same number of columns and rows canot be null.
     * <p/>
     * Please note, this class is holding just reference to the passed values array, so 
     * any change you do to the model via setValue method is actually made in the array.
     *
     * @param values values to be used in this table model. Please note, the values cannot be null
     * and have to be a valid matrix.
     * @throws java.lang.IllegalArgumentException If the values parameter is null, or if it is not a valid rectangular matrix.
     */
    public void setValues(String[][] values) throws IllegalArgumentException {
        // check values validity ...
        checkValues(values);
        this.values = values;
        recomputeModelValues();
        //fireTableModelChanged();
    }

    /**
     * Gets values of the model
     * @return values matrix
     */
    public String[][] getValues() {
        return values;
    }

    /*
    public void setValue(int col, int row, String value) {

    }*/

    /**
     * Gets the value of a table cell at a specified location. Always returns 
     * <code>String</code>.
     * @return value for the given cell coordinates. May return null if there is no value.
     * @param col col index of the value
     * @param row row index of the value
     */
    public Object getValue(int col, int row) {
        return values[row][col];
    }

    /**
     * Decides wheter this table is using headers (column names). This simple
     * model simply checks whether the supplied column names are null and in 
     * such a case this method returns true.
     * @return true if the column names are being supplied and should be visualized, false otherwise
     */	
    public boolean isUsingHeaders() {
        return columnNames != null;
    }

    /**
     * Sets the column names for this model. The array of names
     * should have the same length as the column count.
     * @param columnNames array of names. May be null if the column headers should not be visualized
     */
    public void setColumnNames(String[] columnNames) {
        this.columnNames = columnNames;
        recomputeModelValues();
        //fireTableModelChanged();
    }


    /**
     * Gets the column name for the specified index
     * @param column column index
     * @return column name
     */
    public String getColumnName(int column) {
        if ((columnNames != null) && (column < columnNames.length)) {
                return columnNames[column];
        } else {
                return null;
        }
    }


    /**
     * Fires an event that the values in the table has been changed and
     * the table should be repainted. This method is intended to be used by
     * the user, since the model cannot track changes of
     * values in the supplied arrays.
     */
    //public void fireTableModelChanged() {
    //        Enumeration e = modelListeners.elements();
    //        while (e.hasMoreElements()) {
    //                ((TableModelListener)e.nextElement()).tableModelChanged(this);
    //        }
    //}

    /**
     * Adds a <code>TableModelListener</code> to this instance of the model.
     * @param listener listener to be addded
     */
    //public synchronized void addTableModelListener(TableModelListener listener) {
    //        if (listener != null) {
    //                modelListeners.addElement(listener);
    //        }
    //}

    /**
     * Removes a <code>TableModelListener</code> from this instance of the model.
     * @param listener listener to be removed
     */
    //public synchronized void removeTableModelListener(TableModelListener listener) {
    //        modelListeners.removeElement(listener);
    //}



    /**
     * Checks values if they are rectangular matrix
     */
    private void checkValues(String[][] values) throws IllegalArgumentException {
        rows = 0;
        cols = 0;
        if (values == null) throw new IllegalArgumentException("Values cannot be null.");
        rows = values.length;
        if (rows > 0) {
                cols = values[0].length;
        }
        for (int i=0; i < values.length; i++) {
            String[] row = values[i];
            if (row == null) {
                    throw new IllegalArgumentException("Data cannot contain null rows (row "+i+").");
            } else if (values[0].length != row.length) {
                    throw new IllegalArgumentException("Data cannot contain different row lengths (row "+i+").");
            } 
        }
    }
    //boolean s = false;
    //public void keyPressed(int keyCode){
    //    System.out.println(keyCode);
    //    if (!s){
    //        s = true;
    //        sort(getSelectedCellColumn(), false, true);
    //    }else{
    //        s = false;
    //        sort(getSelectedCellColumn(), true, true);
    //    }
    //}
    
    public void run(){
        
        if (waiting == -1)
            return;
        
        repaint();
        
        if (waiting == 0){
            sort();
        }
        
    }
    
    
    public void sort(/*int columnNumber, boolean goingDown, boolean applyToAllRows*/) throws IllegalArgumentException {
        System.out.println("Sorting..");
        
        if (values == null) throw new IllegalArgumentException("Values cannot be null.");
        
        if (sortColumn/*columnNumber*/ >= getColumnCount()) throw new IllegalArgumentException("Current column does not exist");
        
        if (sortColumn/*columnNumber*/ < 0) throw new IllegalArgumentException("Column number cannot be negative");
        
        //start waiting
        if (waiting == -1){
            waiting = 0;
            curr_thread = new Thread(this);
            curr_thread.start();
            return;   
        }
        
        recomputeModelValues();
        repaint();
        
        Vector vec = new Vector();
        Vector vec2 = new Vector();
        Vector indexes = new Vector();
        
        for (int i = 0; i < values.length; i++){
            vec.addElement(values[i][sortColumn/*columnNumber*/]);
            vec2.addElement(values[i][sortColumn/*columnNumber*/]);
        }
        
        //System.out.println("########   VALUES BEFORE   ##########");
        
        //for (int i =0 ; i < vec.size(); i++)
            //System.out.println(vec.elementAt(i));
        
        for (int j = 0; j < vec.size() - 1; j++)
        for (int i = 0; i < vec.size() - 1; i++){
            boolean end = false;
            int n = 0;
            while(!end){
                int lastn = n;
                if (sortOrder == TableComponentConstants.SORT_ORDER_ASC){
                
                    if (((String)vec.elementAt(i)).toLowerCase().getBytes()[n] > ((String)vec.elementAt(i + 1)).toLowerCase().getBytes()[n]){
                        vec.insertElementAt(vec.elementAt(i), i + 2);
                        vec.removeElementAt(i);
                        end = true;
                    }
                }else{
                    
                    if (((String)vec.elementAt(i)).toLowerCase().getBytes()[n] < ((String)vec.elementAt(i + 1)).toLowerCase().getBytes()[n]){
                        vec.insertElementAt(vec.elementAt(i), i + 2);
                        vec.removeElementAt(i);
                        end = true;
                    }
                }
                
                if (((String)vec.elementAt(i)).toLowerCase().getBytes()[n] == ((String)vec.elementAt(i + 1)).toLowerCase().getBytes()[n]){
                
                    n++;
                }
                
                if (((String)vec.elementAt(i)).toLowerCase().getBytes().length == n){
                    if (sortOrder == TableComponentConstants.SORT_ORDER_DESC){
                        vec.insertElementAt(vec.elementAt(i), i + 2);
                        vec.removeElementAt(i);
                    }
                    end = true;
                }else
                if (((String)vec.elementAt(i + 1)).toLowerCase().getBytes().length == n){
                    if (sortOrder == TableComponentConstants.SORT_ORDER_ASC){
                        vec.insertElementAt(vec.elementAt(i), i + 2);
                        vec.removeElementAt(i);
                    }
                    end = true;
                }
                if (lastn == n) end = true;
            }
        }
        
        
        //System.out.println("########INDEXES#################################");
        
        for (int i = 0 ;i < vec2.size(); i++){
            for (int j = 0 ; j < vec.size(); j++){
                if (vec2.elementAt(i) == vec.elementAt(j) && 
                        isApply(j, indexes)){
                    indexes.addElement(new Integer(j));
                    //System.out.println(((Integer)indexes.lastElement()).intValue());
                    break;
                }
            }
        }
        
        //System.out.println("########   VALUES AFTER   ##########");
        
        //for (int i =0 ; i < vec.size(); i++)
            //System.out.println(vec.elementAt(i));
        
        
        if (sortScope == TableComponentConstants.SORT_SCOPE_ONE){
            
            for (int i = 0 ;i < values.length; i++){
                setValue(sortColumn/*columnNumber*/, i, (String)vec.elementAt(i));
            }
            
        }else{
            
            String[][] s = new String[values.length][values[0].length];
            
            for (int i = 0 ;i < values.length; i++){
                for (int j = 0 ;j < values[0].length; j++){
                    s[i][j] = values[i][j];
                    //System.out.println("s["+i+"]["+j+"]="+s[i][j]);
                }
            }
           
            
            
            
            for (int i = 0 ;i < values[0].length; i++){
                
                //System.out.println("Curr col = " + i);
                
                for (int j = 0 ; j < indexes.size(); j++){
                    
                    //System.out.println("    Curr j = " + j);
                    //System.out.println("        old=" + s[j][i]);
                    //System.out.println("        curr index=" + (Integer)indexes.elementAt(j));
                    
                    setValue(i, ((Integer)indexes.elementAt(j)).intValue(), s[j][i]);
                    
                    //System.out.println("        new=" + values[j][i]);
                }

                //
                //for (int j = 0 ;j < values.length; j++){
                //    System.out.println("    Curr row = " + j);
                //    System.out.println("        old=" + s[j][i]);
                //    System.out.println("        curr index=" + (Integer)indexes.elementAt(j));
                //    setValue(i, j, s[((Integer)indexes.elementAt(j)).intValue()][i]);
                //    System.out.println("        new=" + values[j][i]);
                //}
            }
            
        }
        
        //end of waiting
        waiting = -1;
        repaint();
        
    }
    
    private boolean isApply(int number, Vector vec){
        
        for (int i = 0 ; i < vec.size(); i++){
            if (((Integer)vec.elementAt(i)).intValue() == number)
                return false;
        }
        
        return true;
    }
    
    public void setSortOrder(int order){
        sortOrder = order;
    }
    public void setSortScope(int scope){
        sortScope = scope;
    }
    public void setSortSource(int source){
        sortSource = source;
    }
    public void setSortColumn(int column){
        sortColumn = column;
    }
    
    public void keyPressed(int keyCode){
        System.out.println(keyCode);
        if (keyCode == 49){
            setSortColumn(getSelectedCellColumn());
            setSortScope(TableComponentConstants.SORT_SCOPE_ONE);
            setSortOrder(TableComponentConstants.SORT_ORDER_ASC);
            sort();
        }else
        if (keyCode == 50){
            setSortColumn(getSelectedCellColumn());
            setSortScope(TableComponentConstants.SORT_SCOPE_ONE);
            setSortOrder(TableComponentConstants.SORT_ORDER_DESC);
            sort();
        }else
        if (keyCode == 51){
            setSortColumn(getSelectedCellColumn());
            setSortScope(TableComponentConstants.SORT_SCOPE_ALL);
            setSortOrder(TableComponentConstants.SORT_ORDER_ASC);
            sort();
        }else
        if (keyCode == 52){
            setSortColumn(getSelectedCellColumn());
            setSortScope(TableComponentConstants.SORT_SCOPE_ALL);
            setSortOrder(TableComponentConstants.SORT_ORDER_DESC);
            sort();
        }
       
    }
    
    protected void pointerPressed(int x, int y){
        
        int headerNumber = getSelectedColumn(x, y);
        
        //if returned value is really number of selected column
        if (headerNumber != - 1){
            if (sortColumn == headerNumber){
                if (sortOrder == TableComponentConstants.SORT_ORDER_ASC){
                    setSortOrder(TableComponentConstants.SORT_ORDER_DESC);
                }else{
                    setSortOrder(TableComponentConstants.SORT_ORDER_ASC);
                }
            }else{
                setSortColumn(headerNumber);
                setSortOrder(TableComponentConstants.SORT_ORDER_ASC);
            }
            sort();
            repaint();
            return;
        }
        
        
        if (selectionMode == TableComponentConstants.SELECTION_MODE_CELL){
            int selectedX = getSelectedPenX(x, y);
            int selectedY = getSelectedPenY(x, y);
            
                    
            if (selectedX > cols - 1)
                return;
            if (selectedY > rows - 1)
                return;


            cursorCellX = selectedX;
            cursorCellY = selectedY;
            
            selection = new int[]{selectedY, selectedX};
            
        }else
        if (selectionMode == TableComponentConstants.SELECTION_MODE_SINGLE_ROW){
            
            int selectedY = getSelectedPenY(x, y);
            
                    
            if (selectedY > rows - 1)
                return;

            
            cursorSelectedRows = new int[]{selectedY};
            cursorCellY = selectedY;
            
            
            selection = new int[]{cursorCellY};
            
        }else
        if (selectionMode == TableComponentConstants.SELECTION_MODE_MULTI_ROW){
            
            int selectedY = getSelectedPenY(x, y);
            
                    
            if (selectedY > rows - 1)
                return;

            cursorSelectedRows = new int[]{selectedY};
            cursorCellY = selectedY;
            
            selection = new int[]{cursorCellY};
        }
        
        waiting = 1;
        repaint();
    }
    
    protected void pointerDragged(int x, int y){
        //System.out.println(x + " : " + y);
    }
    
    protected void pointerReleased(int x, int y){
        
        if (selectionMode == TableComponentConstants.SELECTION_MODE_MULTI_ROW){
            
            int selectedY = getSelectedPenY(x, y);
            
                    
            if (selectedY > rows - 1)
                return;


            cursorCellY = selectedY;
            
            if (cursorSelectedRows != null && cursorSelectedRows.length > 0)
            if (cursorSelectedRows[0] != cursorCellY){
                int begY = cursorSelectedRows[0];
                if (begY < cursorCellY){
                    cursorSelectedRows = new int[cursorCellY - begY + 1];
                    for (int i = begY; i <= cursorCellY; i++){
                        cursorSelectedRows[i - begY] = i;                    
                    }
                }else{
                    cursorSelectedRows = new int[-cursorCellY + begY + 1];
                    for (int i = cursorCellY; i <= begY; i++){
                        cursorSelectedRows[i - cursorCellY] = i;                    
                    }
                }
            }
            
            selection = new int[cursorSelectedRows.length];
            
            System.arraycopy(cursorSelectedRows, 0, selection, 0, cursorSelectedRows.length);

            repaint();
        }
        
        waiting = -1;
        
        
    }
    /*
     * if headers are not shown or pointer press not on the columns header method returns -1 otherwise column number
    */
        
    public int getSelectedColumn(int x, int y){
        if (!usingHeaders)
            return -1;
        
        int resX = 0;
        
        int headersHeight = getHeadersFont().getHeight() + DOUBLE_CELL_PADDING;
        
        if (y >= 0 && y <= headersHeight){
            
            if ((tableRows != 0) && (tableCols != 0)) { 
           
                int offsetX = 0;

                for (int i = viewCellX ;i < colWidths.length; i++){

                    if (x > offsetX && x < offsetX + colWidths[i]){

                        resX = i;
                        return resX;
                    }

                    offsetX += colWidths[i];

                }
            }
            
        }
        
        return -1;
        
    }
    
    
    
     public int getSelectedPenX(int x, int y){
        
        int resX = 0;
        //int resY = 0;
        
        int titleHeight = 0;		
        if (title != null) {
                titleHeight = getTitleFont().getHeight() + DOUBLE_CELL_PADDING; // title height
        }

        // get height of headers
        int headersHeight = 0;
        if (usingHeaders) {
                headersHeight =  getHeadersFont().getHeight() + DOUBLE_CELL_PADDING; // headers height
        }
        
        if ((tableRows != 0) && (tableCols != 0)) { 
           
            
            int offsetX = 0;
            
            for (int i = viewCellX ;i < colWidths.length; i++){
                
                if (x > offsetX && x < offsetX + colWidths[i]){
                    
                    resX = i;
                    break;
                }
                
                offsetX += colWidths[i];
                
            }
        }
        
        repaint();
        
        return resX;
        
    }
    
    public int getSelectedPenY(int x, int y){
        
        int resY = 0;
        
        int titleHeight = 0;		
        if (title != null) {
            System.out.println("using title");
                titleHeight = getTitleFont().getHeight() + DOUBLE_CELL_PADDING; // title height
        }

        // get height of headers
        int headersHeight = 0;
        if (usingHeaders) {
                headersHeight =  getHeadersFont().getHeight() + DOUBLE_CELL_PADDING; // headers height
        }
        
        if ((tableRows != 0) && (tableCols != 0)) { 
          
            int offset = 0;
            
            if (title != null) {
                    offset += titleHeight + BORDER_LINE_WIDTH;
            }
            // add headers height if to be drawn
            if (usingHeaders) {
                    offset += headersHeight + BORDER_LINE_WIDTH;
            }
            
            resY = (y - offset) / defaultCellHeight;
            
        }
        
        return resY;
    }
    
    
    
    public boolean isConsistInSelectedRow(int rowNumber){
        if (selectionMode == TableComponentConstants.SELECTION_MODE_MULTI_ROW || selectionMode == TableComponentConstants.SELECTION_MODE_SINGLE_ROW){
            
            if (cursorSelectedRows != null && cursorSelectedRows.length > 0)
            for (int i = 0 ; i < cursorSelectedRows.length; i++){
                if (cursorSelectedRows[i] == rowNumber){
                    return true;
                }
            }
            return false;
        }else{
            return false;
        }
    }
    
    public int getCurrentRow(int x, int y){
        
        return 0;
    }
    
}
